package ddz.net;

import ddz.constants.Crypto;
import ddz.constants.ErrCode;
import ddz.constants.ErrLevel;
import ddz.constants.OpCode;
import ddz.protos.GlobalError;
import ddz.utils.AES;
import ddz.utils.DES;
import ddz.utils.Toolkit;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;

import java.util.Arrays;

public interface WsSender {

    default ByteBuf assemble(int crypto, int opcode, byte[] bytes) {
        if (crypto == 0) {
            ByteBuf buf;
            if (bytes == null || bytes.length == 0) {
                buf = Unpooled.buffer(5);
            } else {
                buf = Unpooled.buffer(5 + bytes.length);
            }
            buf.writeByte(crypto);
            buf.writeInt(opcode);
            if (bytes != null && bytes.length > 0) {
                buf.writeBytes(bytes);
            }
            return buf;
        }
        ByteBuf buf;
        if (bytes == null || bytes.length == 0) {
            buf = Unpooled.buffer(4);
        } else {
            buf = Unpooled.buffer(4 + bytes.length);
        }
        buf.writeInt(opcode);
        if (bytes != null && bytes.length > 0) {
            buf.writeBytes(bytes);
        }
        bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);
        buf.clear();
        if (crypto == 1) {
            bytes = DES.encrypt(bytes, Crypto.NET_DES_KEY_BYTES);
        } else if (crypto == 2) {
            bytes = AES.encrypt(bytes, Crypto.NET_AES_KEY_BYTES);
        }
        buf.capacity(1 + bytes.length);
        buf.writeByte(crypto);
        buf.writeBytes(bytes);
        return buf;
    }

    default boolean sendData(ChannelHandlerContext ctx, ByteBuf sendbuf) {
        if (ctx == null) return false;
        Channel channel = ctx.channel();
        if (channel.isActive() && channel.isWritable() && channel.isOpen()) {
            ctx.writeAndFlush(new BinaryWebSocketFrame(sendbuf).retain());
            return true;
        }
        return false;
    }

    default boolean sendData(ChannelHandlerContext ctx, int crypto, int opcode, byte[] bytes) {
        if (ctx == null) return false;
        Channel channel = ctx.channel();
        if (channel.isActive() && channel.isWritable() && channel.isOpen()) {
            ByteBuf sendbuf = assemble(crypto, opcode, bytes);
            ctx.writeAndFlush(new BinaryWebSocketFrame(sendbuf).retain());
            return true;
        }
        return false;
    }

    default boolean sendData(long uid, int crypto, int opcode, byte[] bytes) {
        if (uid <= 0) return false;
        if (uid < Toolkit.maxInteger()) return false;
        ChannelHandlerContext ctx = CtxManager.instance().get(uid);
        if (ctx == null) return false;
        Channel channel = ctx.channel();
        if (channel.isActive() && channel.isWritable() && channel.isOpen()) {
            ByteBuf sendbuf = assemble(crypto, opcode, bytes);
            ctx.writeAndFlush(new BinaryWebSocketFrame(sendbuf).retain());
            return true;
        }
        return false;
    }

    default boolean sendError(ChannelHandlerContext ctx, int opcode, ErrLevel level, ErrCode code) {
        byte[] errBytes = GlobalError.ScError.newBuilder()
                .setCode(code.getCode())
                .setInfo(code.getInfo())
                .setLevel(level.value())
                .setOpcode(opcode)
                .build().toByteArray();
        return sendData(ctx, 0, OpCode.cmd_error, errBytes);
    }

    default boolean sendError(Long uid, int opcode, ErrLevel level, ErrCode code) {
        if (uid <= 0) return false;
        if (uid < Toolkit.maxInteger()) return false;
        ChannelHandlerContext ctx = CtxManager.instance().get(uid);
        if (ctx == null) return false;
        return sendError(ctx, opcode, level, code);
    }

}
